import { updateEnableStatus2Cfg } from './dataStorage';

// dependsMap 用于存储原数据中externalPath 到 treeData中path的对应关系，方便后面查找元素。
const dependsMap: Map<string, string> = new Map();
/**
 * 结合cfgData和metadata，生成树结构需要的数据格式
 * @param cfgData
 * @param metadata
 */
export function cfg2TreeData(cfgData: ICfgObj, metadata: any): { [key: string]: any } {
  const dataRoot = cfgData.configure.root;
  metadata = metadata.root;
  return [handleCfgNode('root', dataRoot, metadata as ICfgEleMetaData)];
}

function copyAttributes(attributes: any) {
  if (Array.isArray(attributes)) {
    return attributes.map(item => {
      const copied: any = {};
      Object.keys(item).forEach(key => {
        copied[key] = item[key];
      });
      return copied;
    });
  } else if (attributes) {
    const copied: any = {};
    Object.keys(attributes).forEach(key => {
      copied[key] = attributes[key];
    });
    return copied;
  }
}

// 将节点的cfg数据转化为treeNode格式
function handleCfgNode(id: string, data: ICfgBranchNode, metadata: ICfgEleMetaData, parentPath = '', parentExternalPath = '') {
  if (!metadata) {
    return;
  }
  let curExternalPath: string = data['@_externalPath'] || data['@_id'] || '';
  if (!curExternalPath || (curExternalPath && curExternalPath.indexOf('/') === -1)) {
    curExternalPath = parentExternalPath;
  }
  const { label, desc, labelAttribute, attributes, macro, choice, despendSchemaId } = metadata;
  const path = `${parentPath}/${id}`;
  const treeNodeData: { [key: string]: any } = {
    id,
    desc,
    path,
    macro,
    attributes: copyAttributes(attributes),
    choice,
    despendSchemaId,
    externalPath: curExternalPath
  };
  // 仅当当前cfg节点有externalPath属性时，才记录到dependsMap中，方便后期按path查找
  if (data['@_externalPath'] && !dependsMap.get(data['@_externalPath'])) {
    dependsMap.set(data['@_externalPath'], path);
  }

  const children = [];
  const itemLabel = labelAttribute ? (label ? `${data[`@_${labelAttribute}`]}(${label})` : data[`@_${labelAttribute}`]) : label;
  treeNodeData.label = itemLabel;
  for (const dkey in data) {
    if (Object.prototype.hasOwnProperty.call(data, dkey)) {
      if (dkey.startsWith('@_')) {
        const tempKey = dkey.slice(2);
        let foundAttr;
        if (treeNodeData.attributes) {
          foundAttr = (treeNodeData.attributes as any[]).find(attr => attr.name === tempKey);
        }
        if (foundAttr) {
          foundAttr.value = data[dkey];
        } else {
          treeNodeData[tempKey] = data[dkey]; // 去除前缀 @_
        }
      } else {
        if (Array.isArray(data[dkey])) {
          const idAttr = labelAttribute || 'name';
          (data[dkey] as any[]).forEach((item, idx) => {
            children.push(handleCfgNode(`${dkey}_${idAttr}_${idx}`, item, metadata[dkey] as ICfgEleMetaData, path, curExternalPath));
          });
        } else {
          children.push(handleCfgNode(dkey, data[dkey] as ICfgBranchNode, metadata[dkey] as ICfgEleMetaData, path, curExternalPath));
        }
      }
    }
  }
  treeNodeData.children = children;
  return treeNodeData;
}

export function findNodeByPath(path: string, data: any[]) {
  let curData: any = data[0];
  while (curData && curData.path !== path) {
    curData = (curData.children as any[]).find(d => d.path === path || (path.startsWith(d.path) && path.startsWith(`${d.path}/`)));
  }
  return curData;
}

/**
 * 修改数据data的enable字段
 * @param data
 * @param enable
 * @param records 用于存储已修改的数据，后面同步后端用
 */
function changeData(data: any, enable: boolean, records: any[]) {
 if (enable) {
  delete data.enable; // 使能
 } else {
  data.enable = 'false'; // 禁止
 }
 records.push(data); // 记录更新节点
}

/**
 * 更新treeData中的enable状态，并在tree完成所有更新之后，同步cfg数据
 * @param data 发起更新的treeData
 * @param all 整个树的数据，用来查找关联树节点
 * @param enable true = 使能， false = 禁止
 * @param updatedData 记录哪些树节点发生了状态更改，后期一起同步到cfg中
 * @param syncCfg 因为使用了递归，syncCfg标记第一次调用为true，其他调用为false。在所有递归调用都完成后才同步cfg
 * @returns null
 */
export function updateEnableStatus(data: any, all: any[], enable = false, updatedData: any[] = [], syncCfg = true) {
  if (enable) {
    // 如果已使能，则退出
    if (!Object.prototype.hasOwnProperty.call(data, 'enable')) {
      return;
    }
    // step1: 使能自己
    changeData(data, true, updatedData);

    const { path } = data;
    // step2: 使能父节点,直到根
    let lastIdx = (path as string).lastIndexOf('/');
    let parentPath = (path as string).slice();
    let parentNode: any;
    while (lastIdx > 0) {
      parentPath = (parentPath as string).slice(0, lastIdx);
      parentNode = findNodeByPath(parentPath, all);
      if (!parentNode) {
        console.error(`未找到节点${parentPath}`);
      }
      if (!Object.prototype.hasOwnProperty.call(parentNode, 'enable')) {
        break;
      } else {
        changeData(parentNode, true, updatedData);
        lastIdx = (parentPath as string).lastIndexOf('/');
      }
    }
    // step3: 使能子孙节点,遇到choice选择最后一项
    enableChildren(data, updatedData);

    // step4: 检查父节点是否设置了choice属性，如果设置，说明同一时间只能使能一个子节点，也就是说子节点是互斥关系
    parentPath = (path as string).slice(0, (path as string).lastIndexOf('/'));
    if (parentPath) {
      const parentNode = findNodeByPath(parentPath, all);
      if (parentNode && parentNode.choice) {
        (parentNode.choice as string[]).forEach(c => {
          if (c !== data.id) {
            const childNode = parentNode.children.find((item: { id: string }) => item.id === c);
            updateEnableStatus(childNode, all, false, updatedData, false);
          }
        });
      }
    }
    // step5: 检查是否有despendSchemaId属性，如果有说明有依赖项，递归启动依赖项
    const { despendSchemaId } = data;
    if (despendSchemaId && despendSchemaId.length) {
      despendSchemaId.forEach((depend: string) => {
        const dPath = dependsMap.get(depend);
        if (dPath) {
          const dependTreeNode = findNodeByPath(dPath, all);
          if (dependTreeNode) {
            updateEnableStatus(dependTreeNode, all, true, updatedData, false);
          }
        }
      });
    }
  } else {
    // 禁止
    if (data.enable === 'false') {
      return;
    }
    changeData(data, false, updatedData);
    disableAllChild(data, updatedData);
  }
  if (syncCfg) {
    console.log(updatedData);
    updateEnableStatus2Cfg(updatedData);
  }
}

function disableAllChild(data: any, updatedData: any[]) {
  (data.children as any[]).forEach(child => {
    changeData(child, false, updatedData);
    if (child.children) {
      disableAllChild(child, updatedData);
    }
  });
}

function enableChildren(data: any, updatedData: any[]) {
  const choice = data.choice || [];
  if (choice && choice.length) {
    const childCount = (data.children as any[]).length;
    const lastChild = (data.children as any[])[childCount - 1];
    changeData(lastChild, true, updatedData);
    enableChildren(lastChild, updatedData);
  } else {
    (data.children as any[]).forEach(child => {
      changeData(child, true, updatedData);
      if (child.children) {
        enableChildren(child, updatedData);
      }
    });
  }
}

export function getAttrFormData(node: any) {
  const formData: IAttrForm[] = node?.attributes;
  return formData?.map(item => {
    return {
      ...item,
      id: `${node.id}-${item.name}`
    };
  });
}

export function updateTreeNodeAttr(data: any, all: any[], newData: any) {
  const { path } = data;
  const node = findNodeByPath(path, all);
  const { name, value } = newData;
  const attr = node?.attributes.find((item: { name: any }) => item.name === name);
  attr.value = value;
}
